package com.lzs.OkHttpDownload;

import android.os.AsyncTask;
import android.os.Environment;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class downloadTask extends AsyncTask<String, Integer, Integer> {

    /*
    使用AsyncTask实现多线程、异步通信、消息传递(工作线程 & 主线程(UI线程)之间的通信)
    类中参数为3种泛型类型
    整体作用：控制AsyncTask子类执行线程任务时各个阶段的返回类型
    具体说明：
        a. Params：开始异步任务执行时传入的参数类型，对应excute（）中传递的参数
        b. Progress：异步任务执行过程中，返回下载进度值的类型
        c. Result：异步任务执行完成后，返回的结果类型，与doInBackground()的返回值类型保持一致
    注：
        a. 使用时并不是所有类型都被使用
        b. 若无被使用，可用java.lang.Void类型代替
        c. 若有不同业务，需额外再写1个AsyncTask的子类
    参考文档：https://www.jianshu.com/p/ee1342fcf5e7

     */

    public static final int TYPE_SUCCESS = 0;
    public static final int TYPE_FAILED = 1;
    public static final int TYPE_PAUSED = 2;
    public static final int TYPE_CANCLE = 3;

    private String downURL;
    private String downFileName;
    private String fileSavePath;
    File file = null;

    // 判断是否有暂停下载和取消下载
    private boolean isPauseed = false;
    private boolean isCancle = false;

    private downloadListener downloadlistener;


    public downloadTask(downloadListener listener) {
        this.downloadlistener = listener;
    }

    // 方法1：onPreExecute（）
    // 作用：执行 线程任务前的操作
    // 注：根据需求复写
    @Override
    protected void onPreExecute() {
    }

    // 方法2：doInBackground（）
    // 作用：接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
    // 此处通过计算从而实现‘加载进度’
    // 必须复写，从而自定义线程任务
    @Override
    protected Integer doInBackground(String... params) {

        // 自定义的线程任务
        InputStream inputStream = null;
        RandomAccessFile randomAccessFile = null;
        long downloadedLength = 0; //本地已经下载的文件的长度
        downURL = params[0]; //下载地址
        downFileName = downURL.split("/")[downURL.split("/").length - 1]; // 从downURL中获取文件名
        // 获取系统路径，拼接得到文件本地存储路径
        fileSavePath = Environment.getExternalStorageDirectory().getAbsolutePath();
        file = new File(fileSavePath + "/" + downFileName);

        // 如果文件存在，那么获取已经下载到本地的文件的字节数
        if (file.exists()) {
            downloadedLength = file.length();
        }

        // 从网络获取文件的总长度
        long fileContentLength = getContentLength(downURL);

        // 如果没有获取到网络文件的长度
        if (fileContentLength == 0) {
            return TYPE_FAILED;
        } else if (fileContentLength == downloadedLength) {
            return TYPE_SUCCESS;
        }

        // 开始下载,通过回调的Response拿到inputStream做写文件操作
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                // 向服务器添加请求头，用于跳过已经下载到本地的文件
                .addHeader("RANGE", "bytes=" + downloadedLength + "-")
                .url(downURL)
                .build();
        try {
            // 用request新建的call使用execute发起异步请求
            Response response = okHttpClient.newCall(request).execute();
            if (response != null) {
                // 从服务器得到输入流对象
                inputStream = response.body().byteStream();
                // 随机访问文件 rw读写，若果文件不存在则自动创建
                // 参考：https://blog.csdn.net/akon_vm/article/details/7429245
                //       https://www.jianshu.com/p/d101c0078782
                randomAccessFile = new RandomAccessFile(file, "rw");
                // 跳过已经下载到本地的字节,实现暂停功能，即把暂停视为一次新的下载任务
                randomAccessFile.seek(downloadedLength);
                // 设置buffer缓冲，循环写入
                byte[] buffer = new byte[1024];
                int length = 0;
                int total = 0;
                while ((length = inputStream.read(buffer)) != -1) {
                    // 正在读取buffer流时 暂停 or 取消
                    if (isPauseed) {
                        return TYPE_PAUSED;
                    } else if (isCancle) {
                        // 如果取消下载，清空buffer，删除本地文件
                        buffer = null;
                        return TYPE_CANCLE;
                    } else {
                        randomAccessFile.write(buffer, 0, length);
                        total += length;
                        // 计算已经下载的百分比
                        int progress = (int) ((total + downloadedLength) * 100 / fileContentLength);
                        //传递下载进度
                        // 调用publishProgress()显示进度，之后将执行onProgressUpdate()
                        publishProgress(progress);
                    }
                }
                response.body().close();
                return TYPE_SUCCESS;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 如果网络突然中断(或者文件读写产生问题)，则从上一个判断中跳出
        return TYPE_FAILED;
    }

    // 方法3：onProgressUpdate（）
    // 作用：在主线程 显示线程任务执行的进度
    // 根据需求复写
    @Override
    protected void onProgressUpdate(Integer... values) {
        int progress = values[0];
        downloadlistener.onProgress(progress);
    }

    // 方法4：onPostExecute（）
    // 作用：接收线程任务执行结果、将执行结果显示到UI组件
    @Override
    protected void onPostExecute(Integer result) {
        switch (result) {
            case TYPE_SUCCESS:
                downloadlistener.onSuccess();
                break;
            case TYPE_FAILED:
                downloadlistener.onFailed();
                break;
            case TYPE_PAUSED:
                downloadlistener.onPause();
                break;
            case TYPE_CANCLE:
                downloadlistener.onCancle();
                break;
        }
    }

    // 方法5：onCancelled()
    // 作用：将异步任务设置为：取消状态
    // 取消下载，删除本地已经保存的文件，重置进度
    @Override
    protected void onCancelled() {
        // 删除本地文件，参考：https://www.jianshu.com/p/740093143359
        isCancle = true;
        if (isCancle) {
            if (file.isFile() && file.exists()) {
                file.delete();
            }
            downloadlistener.onProgress(0);
        }
    }

    // 通过OkHttp发起一次请求以获得要下载的文件的大小
    public long getContentLength(String downURL) {
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url(downURL)
                .build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (response != null && response.isSuccessful()) {
                long getContentLength = response.body().contentLength();
                return getContentLength;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }

    // 给外部条用控制下载暂停
    // 暂停方法，接收到暂停事件后直接中断doInBackground线程任务的执行
    public void pauseDownload() {
        isPauseed = true;
    }
}